# miku miku dance pmd parser. Does not support bones yet.

import sys
from Sanae3D.lib.ModelObject import Model3D
from Sanae3D.lib.StructReader import StructReader

class PMD_Parser(Model3D):
    
    def __init__(self, obj3D=None, inFile=None, outFile=None):
	'''Initialize a miku miku dance pmd Object.'''
    
	super(PMD_Parser, self).__init__("PMD")
	self.obj3D = obj3D
	self.inFile = inFile
	self.outFile = outFile

    def StringWithNull(self, string):
	pos = string.find("\x00")
	if pos >= 0:
	    return string[:pos]
	else:
	    return string
    
    #
    # t_header = (magic,version,model_name,comment)
    #
    
    def parse_header(self):
	hdr_string = self.inFile.read_string(3)
	hdr_version = self.inFile.read_float()
	model_name = self.inFile.read_string(20)
	model_name = self.StringWithNull(model_name)
	model_name = model_name.decode('shift_jis').encode('UTF-8')
	comment = self.StringWithNull(self.inFile.read_string(256)).decode('shift_jis').encode('UTF-8')
		    
	return model_name
    
    #
    # l_vert = [ ( x,y,z,nx,ny,nz,u,v) ]
    # l_weight = [ ( bone1,bone2,weight,flag ) ]
    #
    def parse_vertices(self, objName):
	vert_size = self.inFile.read_long()
	print "vert = %d" % vert_size
	
	for i in range(vert_size):
	    
	    vx, vy, vz = self.inFile.read_float(3)
	    nx, ny, nz = self.inFile.read_float(3)
	    tu, tv = self.inFile.read_float(2)
	    
	    bone1, bone2 = self.inFile.read_ushort(2)
	    weight, flag = self.inFile.read_ubyte(2)
	    
	    vx = float(vx) * -1.0
	
	    self.create_vertex(objName, i)
	    self.add_coords(objName, i, [vx, vy, vz])
	    self.add_vert_normals(objName, i, [nx, ny, nz])
	    self.add_vert_uv(objName, i, [tu, tv])
    #
    # l_poly = [ vertex number ] 
    #
    def parse_faces(self, objName):
	
	numVerts = self.inFile.read_long()
	if numVerts % 3 == 0:
	    numFaces = numVerts / 3
	    print "%d faces" %numFaces
	    for i in range(numFaces):
		v1, v2, v3 = self.inFile.read_ushort(3)
		self.create_face(objName, i)
		self.add_face_verts(objName, i, [v1, v2, v3])
	else:
	    print "quads"
	    
    def set_material(self, matNum, offset, faces, objName):
	
	for i in range(offset, offset + faces):
	    self.add_face_material(objName, i, matNum)
    
    # [ ( r,g,b,a, shininess, specular(r,g,b), ambient(r,g,b) , toon, toon_edge , edges, file ) ]
    #
    def parse_materials(self, objName):
	
	matCount = self.inFile.read_long()
	print "material = %d" %matCount
	offset = 0
	for i in range(matCount):
	    r, g, b, a = self.inFile.read_float(4)
	    shininess = self.inFile.read_float()
	    Sr, Sg, Sb = self.inFile.read_float(3)
	    Ar, Ag, Ab = self.inFile.read_float(3)
	    t_toon_tex  = self.inFile.read_byte(1)
	    t_toon_edge  = self.inFile.read_byte(1)
	    faces = self.inFile.read_long() / 3
	    texName = self.StringWithNull(self.inFile.read_string(20))
	    self.create_material(i)
	    self.add_material_name(i, "material00%d" %i)
	    self.add_texture_name(i, texName)
	    self.add_ambient(i, [Ar, Ag, Ab])
	    self.add_specular(i, [Sr, Sg, Sb])
	    self.add_rgba(i, [r,g,b,a])
	    self.add_power(i, shininess)
	    
	    #set material to faces
	    self.set_material(i, offset, faces, objName)
	    offset += faces
    
    def parse_bones(self, objName):
	
	numBones = self.inFile.read_short()
	
	print "%d bones" %numBones
	
	for i in range(numBones):
	    name = self.StringWithNull(self.inFile.read_string(20))

    #
    # [ ( name, parent , to , kind , knum, x,y,z ) ] 
    #
    def ReadBone(f,d_pmd):
	list = []
	size = ReadStruct(f,"H")[0]
	print "Bone = %d" % size
	
	for i in range(size):
	    name = StringWithNull(ReadStruct(f,"20s")[0]).decode('shift_jis').encode('UTF-8')
	    t_data = ReadStruct(f,"HH")
	    t_kind = ReadStruct(f,"B")
	    t_knum = ReadStruct(f,"H")
	    t_pos  = ReadStruct(f,"3f")
	    
	    t_data = t_data + t_kind + t_knum + t_pos
	    
	    list.append((name,) + t_data)
	    #print (name,) + t_data
	
	d_pmd["Bone"] = list
    #
    # [ ( ik,target,p1,p2,[node]) ]
    #
    def ReadIK(f,d_pmd):
	list = []
	size = ReadStruct(f,"H")[0]
	print "IK = %d" % size
	
	for i in range(size):
	    t_parm1 = ReadStruct(f,"2H")
	    t_parm2 = ReadStruct(f,"B")
	    t_parm3 = ReadStruct(f,"H")
	    t_parm4 = ReadStruct(f,"f")
    
	    l_bone = []
	    for j in range(t_parm2[0]):
		node = ReadStruct(f,"H")[0]
		l_bone.append(node)
    
    
	    list.append(t_parm1 + t_parm3 + t_parm4 + (l_bone,))
	
	d_pmd["IK"] = list
    
    #
    # [ ( name,type,[vertex]) ]
    #
    def ReadSkin(f,d_pmd):
	list = []
    
	size = ReadStruct(f,"H")[0]
	print "Skin = %d" % size
	
	for i in range(size):
	    name = StringWithNull(ReadStruct(f,"20s")[0]).decode('shift_jis').encode('UTF-8')
	    skin_size = ReadStruct(f,"L")[0]
	    t_parm = ReadStruct(f,"B")
	    
	    l_vert = []
	    for j in range(skin_size):
		t_vdata = ReadStruct(f,"L3f")
		l_vert.append(t_vdata)
		    
	    list.append((name,) + t_parm + (l_vert,))
	
	d_pmd["Skin"] = list
    
    #
    # [ ( name,type,[vertex]) ]
    #
    def ReadSkinIndex(f,d_pmd):
	list = []
    
	size = ReadStruct(f,"B")[0]
	print "SkinIndex = %d" % size
	
	for i in range(size):
	    skin_index = ReadStruct(f,"H")[0]
	    list.append(skin_index)
	
	d_pmd["SkinIndex"] = list
    
    #
    # [ name ]
    #
    def ReadDispName(f,d_pmd):
	list = []
    
	size = ReadStruct(f,"B")[0]
	print "DispName = %d" % size
	
	for i in range(size):
	    name = StringWithNull(ReadStruct(f,"50s")[0]).decode('shift_jis').encode('UTF-8')
	    list.append(name)
	
	d_pmd["DispName"] = list
    
    #
    # [ ( bone , type ) ]
    #
    def ReadBoneIndex(f,d_pmd):
	list = []
    
	size = ReadStruct(f,"L")[0]
	print "BoneIndex = %d" % size
	
	for i in range(size):
    
	    bone = ReadStruct(f,"H")[0]
	    type = ReadStruct(f,"B")[0]
	    
	    list.append((bone,type))
	
	d_pmd["BoneIndex"] = list
    
    
    def EncString(string):
	enc = sys.getfilesystemencoding()
	
	return unicode(string,"shift-jis").encode(enc,"replace")
    
    def parse_file(self):
	
	objName = self.parse_header()
	objName = "obj"

	self.create_object(objName)
	
	self.parse_vertices(objName)
	self.parse_faces(objName)
	self.parse_materials(objName)
	self.parse_bones(objName)
	#ReadBone(f,d_pmd)
	
	#ReadIK(f,d_pmd)
	#ReadSkin(f,d_pmd)
	#ReadSkinIndex(f,d_pmd)
	#ReadDispName(f,d_pmd)
	#ReadBoneIndex(f,d_pmd)
    
def read_file(path):
    
    openFile = StructReader(open(path, 'rb'))
    obj = PMD_Parser(inFile=openFile)
    obj.parse_file()
    openFile.close()
    return obj

def definitions():
    
    return "Pmd", "PMD", "Miku Miku Dance PMD"

if __name__ == '__main__':
    filename = "reimu_F01.pmd" 
    obj = read_file(filename)